#include "log.h"
#include "utility.h"
#include <time.h>
#include <errno.h>
#include <error.h>

static log_cb log_fn = NULL;
static log_cb log_debug_fn = NULL;
static FILE *logfile = NULL;
static FILE *logdebugfile = NULL;

typedef void (*event_fatal_cb)(int err);
static event_fatal_cb fatal_fn = NULL;
static void event_log(int severity, const char *msg);

void event_set_fatal_callback(event_fatal_cb cb)
{
	fatal_fn = cb;
}
void _add_logx(const char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	warn_helper(_EVENT_LOG_MSG, NULL, NULL, 0, fmt, ap);
	va_end(ap);
}

void _add_debug_logx(const char *file, const char *func, int line, const char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	warn_helper(_EVENT_LOG_DEBUG, file, func, line, fmt, ap);
	va_end(ap);
}
static void
event_exit(int errcode)
{
	if (fatal_fn)
	{
		fatal_fn(errcode);
		exit(errcode); /* should never be reached */
	}
	else if (errcode == _EVENT_ERR_ABORT)
		abort();
	else
		exit(errcode);
}
void warn_helper(int severity, const char *file, const char *func, int line,
				 const char *fmt, va_list ap)
{
	char buf[1024];
	ssize_t len = 0;

	time_t timep;
	struct tm *p_tm;
	timep = time(NULL);
	p_tm = gmtime(&timep);
	len += snprintf(buf, 1024, "%d-%d-%d %d:%d:%d\n", (p_tm->tm_year + 1900), (p_tm->tm_mon + 1), p_tm->tm_mday, p_tm->tm_hour, p_tm->tm_min, p_tm->tm_sec);

	if (severity == _EVENT_LOG_DEBUG)
	{
		len += snprintf(buf + len, 1024, ">\tin file:%s,function:%s,line:%d\n>\t", file, func, line);
	}
	if (fmt != NULL)
	{
		util_vsnprintf(buf + len, sizeof(buf) - len, fmt, ap);
	}

	//buf[len + 1] = '\0';
	wirte_to_log(severity, buf);

}

void wirte_to_log(int severity, const char *msg)
{
	if (log_fn && logfile){
        log_fn(severity, msg);
    }
	else if (log_debug_fn && logdebugfile){
		log_debug_fn(severity, msg);
    }
	else
	{
		const char *severity_str;
		switch (severity)
		{
		case _EVENT_LOG_DEBUG:
			severity_str = "debug";
			break;
		case _EVENT_LOG_MSG:
			severity_str = "msg";
			break;
		case _EVENT_LOG_WARN:
			severity_str = "warn";
			break;
		case _EVENT_LOG_ERR:
			severity_str = "err";
			break;
		default:
			severity_str = "???";
			break;
		}
		(void)fprintf(stderr, "[%s] %s\n", severity_str, msg);
	}
}

static void write_to_file_cb(int severity, const char *msg)
{
	const char *s;
	if (severity == _EVENT_LOG_MSG && (!log_fn || !logfile))
		return;
	else if (severity == _EVENT_LOG_DEBUG && (!log_debug_fn || !logdebugfile))
		return;
	switch (severity)
	{
	case _EVENT_LOG_DEBUG:
		s = "debug";
		break;
	case _EVENT_LOG_MSG:
		s = "msg";
		break;
	case _EVENT_LOG_WARN:
		s = "warn";
		break;
	case _EVENT_LOG_ERR:
		s = "error";
		break;
	default:
		s = "?";
		break; /* never reached*/
	}
	//puts(msg);
	if (severity == _EVENT_LOG_MSG)
	{
		fprintf(logfile, "[%s] %s\n", s, msg);
		fflush(logfile);
	}
	else
	{
		fprintf(logdebugfile, "[%s] %s\n", s, msg);
		fflush(logdebugfile);
	}
}
static void
_warn_helper(int severity, const char *errstr, const char *fmt, va_list ap)
{
	char buf[1024];
	size_t len;

	if (fmt != NULL)
		util_vsnprintf(buf, sizeof(buf), fmt, ap);
	else
		buf[0] = '\0';

	if (errstr)
	{
		len = strlen(buf);
		if (len < sizeof(buf) - 3)
		{
			util_snprintf(buf + len, sizeof(buf) - len, ": %s", errstr);
		}
	}
	event_log(severity, buf);
}
void event_errx(int eval, const char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	_warn_helper(_EVENT_LOG_ERR, NULL, fmt, ap);
	va_end(ap);
	event_exit(eval);
}

int32_t init_log_file()
{
	char *logFile = "runing.log";
	char *dbgFile = "debug.log";
	logfile = fopen(logFile, "a+");
	if (logfile == NULL)
	{
		fprintf(stderr, "when fopen %s:%s\n", logFile, strerror(errno));
		return -1;
	}
	logdebugfile = fopen(dbgFile, "a+");
	if (logdebugfile == NULL)
	{
		fprintf(stderr, "when fopen %s:%s\n", dbgFile, strerror(errno));
		return -1;
	}
	log_fn = write_to_file_cb;
	log_debug_fn = write_to_file_cb;
	return 0;
}
static void
event_log(int severity, const char *msg)
{
	if (log_fn)
		log_fn(severity, msg);
	else
	{
		const char *severity_str;
		switch (severity)
		{
		case _EVENT_LOG_DEBUG:
			severity_str = "debug";
			break;
		case _EVENT_LOG_MSG:
			severity_str = "msg";
			break;
		case _EVENT_LOG_WARN:
			severity_str = "warn";
			break;
		case _EVENT_LOG_ERR:
			severity_str = "err";
			break;
		default:
			severity_str = "???";
			break;
		}
		(void)fprintf(stderr, "[%s] %s\n", severity_str, msg);
	}
}
